#
# Unpacker for Dean Edward's p.a.c.k.e.r, a part of javascript beautifier
# by Einar Lielmanis <einar@jsbeautifier.org>
#
#     written by Stefano Sanfilippo <a.little.coder@gmail.com>
#
# usage:
#
# if detect(some_string):
#     unpacked = unpack(some_string)
#

"""Unpacker for Dean Edward's p.a.c.k.e.r"""

import re
import string
from jsbeautifier.unpackers import UnpackingError

PRIORITY = 1

def detect(source):
    """Detects whether `source` is P.A.C.K.E.R. coded."""
    return source.replace(' ', '').startswith('eval(function(p,a,c,k,e,')

def unpack(source):
    """Unpacks P.A.C.K.E.R. packed js code."""
    payload, symtab, radix, count = _filterargs(source)

    if count != len(symtab):
        raise UnpackingError('Malformed p.a.c.k.e.r. symtab.')

    try:
        unbase = Unbaser(radix)
    except TypeError:
        raise UnpackingError('Unknown p.a.c.k.e.r. encoding.')

    def lookup(match):
        """Look up symbols in the synthetic symtab."""
        word  = match.group(0)
        if word == '直达链接':
            # print('###%s###' %word)
            return word
        # print(symtab[unbase(word)] or word)
        return symtab[unbase(word)] or word

    source = re.sub(r'\b\w+\b', lookup, payload)
    # source = re.sub(r'\b[\s\S]+\b', lookup, payload)
    return _replacestrings(source)

def _filterargs(source):
    """Juice from a source file the four args needed by decoder."""
    juicers = [ (r"}\('(.*)', *(\d+), *(\d+), *'(.*)'\.split\('\|'\), *(\d+), *(.*)\)\)"),
                (r"}\('(.*)', *(\d+), *(\d+), *'(.*)'\.split\('\|'\)"),
              ]
    for juicer in juicers:
        args = re.search(juicer, source, re.DOTALL)
        if args:
            a = args.groups()
            try:
                return a[0], a[3].split('|'), int(a[1]), int(a[2])
            except ValueError:
                raise UnpackingError('Corrupted p.a.c.k.e.r. data.')

    # could not find a satisfying regex
    raise UnpackingError('Could not make sense of p.a.c.k.e.r data (unexpected code structure)')



def _replacestrings(source):
    """Strip string lookup table (list) and replace values in source."""
    match = re.search(r'var *(_\w+)\=\["(.*?)"\];', source, re.DOTALL)

    if match:
        varname, strings = match.groups()
        startpoint = len(match.group(0))
        lookup = strings.split('","')
        variable = '%s[%%d]' % varname
        for index, value in enumerate(lookup):
            source = source.replace(variable % index, '"%s"' % value)
        return source[startpoint:]
    return source


class Unbaser(object):
    """Functor for a given base. Will efficiently convert
    strings to natural numbers."""
    ALPHABET  = {
        53 : '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQ',
        59 : '0123456789abcdefghijklmnopqrstuvwABCDEFGHIJKLMNOPQRSTUVWXYZ',
        62 : '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
        95 : (' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ'
              '[\]^_`abcdefghijklmnopqrstuvwxyz{|}~')
    }

    def __init__(self, base):
        self.base = base

        # If base can be handled by int() builtin, let it do it for us
        if 2 <= base <= 36:
            self.unbase = lambda string: int(string, base)
        else:
            # Build conversion dictionary cache
            try:
                self.dictionary = dict((cipher, index) for
                    index, cipher in enumerate(self.ALPHABET[base]))
            except KeyError:
                raise TypeError('Unsupported base encoding.')

            self.unbase = self._dictunbaser

    def __call__(self, string):
        return self.unbase(string)

    def _dictunbaser(self, string):
        """Decodes a  value to an integer."""
        ret = 0
        for index, cipher in enumerate(string[::-1]):
            ret += (self.base ** index) * self.dictionary[cipher]
        return ret

if __name__ == '__main__':
    js1 = '''eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('5 D(I){3 8,G=V 1X("(^| )"+I+"=([^;]*)(;|$)");7(8=d.1j.1m(G)){k 1x(8[2])}x{k\'\'}}5 u(v,F,S,u){k v.J(0,F-1)+u+v.J(S,v.1i)}3 E=b.c.t;3 6=D("6");7(6!=\'\'){6=1d("("+6+")");w=6.w;p=6.p;7(E.B(w+"/"+p)<0){H=u(E,1o,1w,w+"/"+p);b.c.1a=H}}(5(){(5(i,s,o,g,r,a,m){i[\'1s\']=r;i[r]=i[r]||5(){(i[r].q=i[r].q||[]).1v(1r)},i[r].l=1*V 1h();a=s.L(o),m=s.1g(o)[0];a.1l=1;a.X=g;m.1p.1W(a,m)})(b,d,\'1R\',\'//h.1Q-R.4/R.1A\',\'9\');3 T=D(\'1P\');3 8=T.1T(\'|\');7(8[1]){9(\'P\',\'O-N-1\',\'M\',{\'1U\':8[1]})}x{9(\'P\',\'O-N-1\',\'M\')}3 y=Q;3 U=Q;5 A(){7(y)k;y=1V;7(U){3 e=d.L(\'1N\');e.X=1b;e.1F.1E=\'1D\';d.K.1B(e);b.16(5(){d.K.1H(e);b.c.t=f},W)}x{b.c.t=f}}9(\'1I\',\'1S\',c.t);9(\'19\',\'1J\');f=\'j://h.n.4/12.11?10=Y&Z=j%13%14%18.C.4%17.15\';3 z=1K.1L.1G();7(z.B(\'1c/1C\')>0||z.B(\'1c/1M\')>0){f=f.1a(\'j://h.n.4\',\'1Y://h.n.4\')}1b=\'j://h.n.4/12.11?10=Y&Z=j%13%14%18.C.4%17.15\';9(\'19\',\'1t\',\'直达链接\',\'1u\',\'1y\',{\'1z\':\'1n\',\'1f\':\'1k\',\'1O\':\'0\',\'1q\':\'C.4\',\'1e\':A});16(A,W)})()',62,123,'|||var|com|function|zdm_track_info|if|arr|ga||window|location|document|ifr|smzdmhref||www||https|return|||linkstars||channel||||href|changeStr|allstr|source|else|redirected|uaStr|redirect|indexOf|jd|getCookie|this_url|start|reg|go_url|name|substring|body|createElement|auto|27058866|UA|create|false|analytics|end|cookie_user|is_amazon|new|1000|src|156_0_184__ca_aa_fx_1515_0|to|feedback|php|click|3A|2F|html|setTimeout|2F4432407|2Fitem|send|replace|smzdmhref1|chrome|eval|hitCallback|dimension6|getElementsByTagName|Date|length|cookie|1515|async|match|aa|26|parentNode|dimension30|arguments|GoogleAnalyticsObject|event|ca|push|30|unescape|fx|dimension29|js|appendChild|53|none|display|style|toLowerCase|removeChild|set|pageview|navigator|userAgent|54|iframe|dimension1|user|google|script|page|split|userId|true|insertBefore|RegExp|http'.split('|'),0,{}))'''
    print(unpack(js1))
